package com.lhj.queue;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @ClassName WheelQueue
 * @Description 像轮子一样转动的队列：环形队列、循环队列。
 * @date 2017-03-20
 * @author hongjian.liu
 * @version 1.0.0
 *
 */
public class WheelQueue {

	private static final Logger LOG = LoggerFactory.getLogger(WheelQueue.class);
	private static final int DEFAULT_QUEUE_SIZE = 3600;

	/** 任务Id对应的任务属性 */
	private final Map<String, TaskAttribute> attrMap = new HashMap<>(1000, 1F);

	/** 建立一个有3600个槽位的环形队列； 每秒轮询一个槽位，3600个就是3600秒=1小时 */
	private final Slot[] slotArray;

	public WheelQueue(){
		slotArray = new Slot[DEFAULT_QUEUE_SIZE];
		for (int i = 0; i < DEFAULT_QUEUE_SIZE; i++) {
			this.slotArray[i] = new Slot();
		}
	}
	
	/**
	 * 添加一个任务到环形队列
	 * 
	 * @param task  任务
	 * @param secondsLater  以当前时间点为基准，多少秒以后执行
	 */
	public void add(final AbstractTask task, int secondsLater) {
		//设置任务属性
		// int slotIndex = TaskAttributeUtil.setAttribute(secondsLater, task, taskSlotMap);

		Calendar calendar = Calendar.getInstance();
		//把当前时间的分钟和秒加起来
		int currentSecond = calendar.get(Calendar.MINUTE) * 60 + calendar.get(Calendar.SECOND);
		int slotIndex = (currentSecond + secondsLater) % DEFAULT_QUEUE_SIZE;

		task.setCycleNum(secondsLater / DEFAULT_QUEUE_SIZE);
		calendar.add(Calendar.SECOND, 1);

		TaskAttribute taskAttr = new TaskAttribute(slotIndex, calendar.getTime(), new Date());
		addTask(taskAttr, task);
		LOG.debug("add task. task={}, slotIndex={}", task.toString() + slotIndex);
	}

	/**
	 * 根据指定索引获取槽位中的数据。但不删除。
	 * 
	 * @param index
	 * @return
	 */
	public Slot peek(int index) {
		return slotArray[index];
	}

	private void addTask(TaskAttribute taskAttr, AbstractTask task){
		attrMap.put(task.getId(), taskAttr);
		slotArray[taskAttr.getSlotIndex()].add(task); //加到对应槽位的集合中
	}

	/**
	 * 根据id取消(即移除)一个任务
	 * 
	 * @param taskId   任务id
	 */
	public void cancel(String taskId) {
		TaskAttribute taskAttr = removeAttr(taskId);
		if (taskAttr != null) {
			removeTask(taskAttr.getSlotIndex(), taskId);
		}
	}

	public TaskAttribute removeAttr(String taskId){
		return attrMap.remove(taskId);
	}

	private void removeTask(int slotIndex, String taskId){
		slotArray[slotIndex].remove(taskId);
	}

}
